/*CS 149 Homework 2 - Summer 2013
Authors:
	Jiajie Wu
	Jiangcun Chilie
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#define SIMULATION_TIME_LIMIT 60
#define WAIT_TIME_LIMIT 10
#define TRUE    1
#define FALSE   0

typedef int bool;
typedef struct student student;

struct student {
	int arrive_time;
	int visit_time;
};

//global counter for simulation time
int i;

int visit_student;
int wait_student;
bool is_in_meet = FALSE;
bool is_visiting = FALSE;

pthread_t current_tid;
pthread_t wait_tid[3];
pthread_mutex_t mutex;
int student_wait_time[3];
int teacher_work_time;
bool has_student_leave;


bool is_student_come();
void offer(pthread_t);
pthread_t poll();
void empty_wait_list();
void wait_time_increment();
void check_wait_time();
void *simulate(void* param);

	
main() {
	//Print Tabular Format Head
	printf("Current Time\t|Visiting\t|Waiting\t|Event\n");
	printf("--------------------------------------------------------------------------------\n");
	//Initialize the locks
	pthread_mutex_init(&mutex, NULL);
	
	//Initialize Student Counts and Prof. work time
	visit_student = 0;
	wait_student = 0;
	teacher_work_time = 0;
	
	//Initialize student wait 
	int wait_time_count;
	for (wait_time_count = 0; wait_time_count < 3; wait_time_count++) {
		student_wait_time[wait_time_count] = 0;
	}
	
	has_student_leave = FALSE;
	
	//Simulating the 1 hour office time
	for (i = 1; i <= 70; i++) {
		
		if (i <= SIMULATION_TIME_LIMIT) {
			if (is_student_come() && wait_student < 3) {
				student s;
				s.arrive_time = i;
				int v_time = (rand() % 5) + 1;
				char v_string[3];
				sprintf(v_string, "%d", v_time);
				s.visit_time = v_time;
			
				
				//printf("New Thread run time is %d\n", v_time);
				pthread_t tid;
				pthread_attr_t attr;
				pthread_attr_init(&attr);
				pthread_create(&tid, &attr, simulate, &v_string);
				offer(tid);
				fflush(stdout);
				sleep(1);
				printf("%d\t\t|%d\t\t|%d", i, visit_student, wait_student);
				printf("\t\t|Student arrive, v_time: %d\n", v_time);
				
				check_wait_time();
				wait_time_increment();
			} else {
				fflush(stdout);
				sleep(1);
				printf("%d\t\t|%d\t\t|%d", i, visit_student, wait_student);
				printf("\t\t|No Student Arrive\n");
				
				check_wait_time();
				wait_time_increment();
			}
			
		} else if (i > SIMULATION_TIME_LIMIT) {
			if (is_visiting) {
				fflush(stdout);
				sleep(1);
				if (i == SIMULATION_TIME_LIMIT + 1) {
					empty_wait_list();
					printf("%d\t\t|%d\t\t|%d", i, visit_student, wait_student);
					printf("\t\t|All Waiting student left.\n");
					
					check_wait_time();
					wait_time_increment();
				} else {
					printf("%d\t\t|%d\t\t|%d\n", i, visit_student, wait_student);
					
					check_wait_time();
					wait_time_increment();
				}
			} else {
				
				printf("Simulation Terminated\n");
				fflush(stdout);
				sleep(1);
				break;
			}
		}
		
		printf("--------------------------------------------------------------------------------\n");
		if (visit_student == 0) {
			teacher_work_time++;
		}
	}
	if (teacher_work_time >= 2) {
		printf("Professor got chance to work on the programming Language.\n");
		printf("Working time is %d\n", teacher_work_time-1);
	}
	
	if (!has_student_leave) {
		printf("Waiting time of each student is less than 10 min. So no student left during waiting\n");
	}
}

void *simulate(void *param) {
	int v_time = *((int *) param) - '0';
	int oldtype;
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
	pthread_mutex_lock(&mutex);
	if (i > SIMULATION_TIME_LIMIT) {
		pthread_exit(0);
	} 
	//queue operations
	current_tid = poll();
	visit_student = 1;
	is_visiting = TRUE;
	printf("\t\t|\t\t|\t\t|Student Start Visiting, v_time: %d\n", v_time);
	int k;
	for (k = 1; k <= v_time; k++) {
		
		fflush(stdout);
		sleep(1);
	}
	
	visit_student = 0;
	is_visiting = FALSE;
	pthread_mutex_unlock(&mutex);
	pthread_exit(0);
}

/*Decide if student come to the office at a certain time slice.
At each time slice, a student may appear at 40% chance*/
bool is_student_come() {
	int is_come = rand() % 10 + 1;
	if (is_come > 6) return TRUE;
	else return FALSE;
}

void offer(pthread_t t) {
	if (wait_student < 3) {
		wait_tid[wait_student] = t;
		student_wait_time[wait_student] = 0;
		wait_student++;
	} 
}

pthread_t poll() {
	pthread_t t;
	if (wait_student > 0) {
		t = wait_tid[0];
		int i;
		for (i = 0; i < wait_student; i++) {
			if (i < 2) {
				wait_tid[i] = wait_tid[i+1];
				student_wait_time[i] = student_wait_time[i+1];
			}
		}
		wait_student--;
		return t;
	} else {
		printf("Waiting queue empty. Nothing to poll.\n");
		return 0;	
	}
}

void empty_wait_list() {
	int j;
	for (j = 0; j < wait_student; j++) {
		pthread_t w_tid = wait_tid[j];
		pthread_cancel(w_tid);
	}
	wait_student = 0;
}

void wait_time_increment() {
	int w_count;
	for (w_count = 0; w_count < wait_student; w_count++) {
		student_wait_time[w_count]++;
	}
}

void check_wait_time() {
	int temp;
	//printf("\t\t|\t\t|\t\t|%d: %d-%d-%d\n", wait_student, student_wait_time[0],student_wait_time[1], student_wait_time[2]);
	printf("\t\t|\t\t|\t\t|Student Wait Time: ");
	int w_count;
	for (w_count = 0; w_count < wait_student; w_count++) {
		printf("%d ", student_wait_time[w_count]);
	}
	printf("\n");
	if (wait_student > 0) {
		
		if (student_wait_time[0] == WAIT_TIME_LIMIT) {
			
			pthread_t w_tid = poll();
			pthread_cancel(w_tid);
			printf("\t\t|\t\t|\t\t|Wait time over, student left\n");
			has_student_leave = TRUE;
		}
	}
}
